﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.ServiceModel;
using SecurityFoundation;
using System.Web.Security;
using SecurityFoundation.Entities;

namespace SecurityAgent
{
    [SecurityExceptionHandler]
    public class RoleProviderService : IRoleProviderService
    {
        public bool IsAuthorizedAdmin(string applicationKey, string userToken)
        {
            //string userToken = OperationContext.Current.IncomingMessageHeaders.GetHeader<string>("UserToken", "ns");
            if (String.IsNullOrEmpty(userToken)) return false;

            ApplicationDefination app = Repository.GetApplicationDefinition(new Guid(applicationKey));
            Realm realm = Repository.GetRealm(app.Realm);

            return IsUserInRole(realm.AdminApplication.ToString(), userToken, string.Empty, Constant.ADMIN_ROLE);
        }

        protected System.Web.Security.RoleProvider GetProvider(string applicationKey)
        {
            System.Web.Security.RoleProvider provider;

            if (applicationKey == null)
            {
                throw new FaultException("No Application Specified");    
            }

            ApplicationDefination appDef = Repository.GetApplicationDefinition(new Guid(applicationKey));

            if (appDef == null) return null;

            Realm realm = Repository.GetRealm(appDef.Realm);

            if (realm.RoleProviderName != null)
            {
                provider = Roles.Providers[realm.RoleProviderName];
            }
            else
                provider = Roles.Provider;

            return provider;

        }


        public void AddUsersToRoles(string applicationKey,  string operatorToken, string[] usernames, string[] roleNames)
        {
            if (!IsAuthorizedAdmin(applicationKey, operatorToken))
                throw new FaultException<UnauthorizedAccessFault>(new UnauthorizedAccessFault{ Application = applicationKey, Object="Roles", Operation="AddUsersToRoles"});

            ApplicationDefination appDef = Repository.GetApplicationDefinition(new Guid(applicationKey));
            if( appDef.UseRealmRole != (int) UseRealmRoleRequirement.Allways )
                throw new FaultException(new FaultReason("Application must use realm roles to allow this operation"));

            GetProvider(applicationKey).AddUsersToRoles(usernames, roleNames);
        }

        public void CreateRole(string applicationKey, string operatorToken, string roleName)
        {
            if (!IsAuthorizedAdmin(applicationKey, operatorToken))
                throw new FaultException<UnauthorizedAccessFault>(new UnauthorizedAccessFault{ Application = applicationKey, Object="Roles", Operation="CreateRole"});
            
            ApplicationDefination appDef = Repository.GetApplicationDefinition(new Guid(applicationKey));
            if( appDef.UseRealmRole != (int) UseRealmRoleRequirement.Allways )
                throw new FaultException(new FaultReason("Application must use realm roles to allow this operation"));

            GetProvider(applicationKey).CreateRole(roleName);
        }

        public void DeleteRole(string applicationKey, string operatorToken, string roleName, bool throwOnPopulatedRole)
        {
            if (!IsAuthorizedAdmin(applicationKey, operatorToken))
                throw new FaultException<UnauthorizedAccessFault>(new UnauthorizedAccessFault{ Application = applicationKey, Object="Roles", Operation="DeleteRole"});
            
            ApplicationDefination appDef = Repository.GetApplicationDefinition(new Guid(applicationKey));
            if( appDef.UseRealmRole != (int) UseRealmRoleRequirement.Allways )
                throw new FaultException(new FaultReason("Application must use realm roles to allow this operation"));

            GetProvider(applicationKey).DeleteRole(roleName, throwOnPopulatedRole);
        }

        public string[] FindUsersInRole(string applicationKey,  string operatorToken, string roleName, string usernameToMatch)
        {
            if (!IsAuthorizedAdmin(applicationKey, operatorToken))
                throw new FaultException<UnauthorizedAccessFault>(new UnauthorizedAccessFault{ Application = applicationKey, Object="Roles", Operation="DeleteRole"});
           
            string[] roleList = Repository.GetRealmRoleName(new Guid(applicationKey), roleName);

            List<string> ret = new List<string>();

            if( roleList == null || roleList.Count() == 0 ) return null;

            RoleProvider provider = GetProvider(applicationKey);

            foreach( string role1 in roleList)
            {
                string[] users = provider.FindUsersInRole(role1, usernameToMatch);

                ret.AddRange(users);
            }

            return ret.ToArray();
            
        }

        public RoleCollection GetAllRoles(string applicationKey,  string operatorToken)
        {
            if (!IsAuthorizedAdmin(applicationKey, operatorToken))
                throw new FaultException<UnauthorizedAccessFault>(new UnauthorizedAccessFault{ Application = applicationKey, Object="Roles", Operation="DeleteRole"});
           
            ApplicationDefination app = Repository.GetApplicationDefinition(new Guid(applicationKey));

            string[] realmRoles = GetProvider(applicationKey).GetAllRoles();

            List<string> roleList = new List<string>();

            if( app.UseRealmRole == (int) UseRealmRoleRequirement.Allways) 
            {
                roleList.AddRange( realmRoles);
            }
            else 
            {
                ApplicationRoleMapping[] mappings = Repository.GetApplicationRoleMappings(app.ApplicationKey);

                if( app.UseRealmRole == (int) UseRealmRoleRequirement.NotAllowed )
                {
                    if( mappings != null )
                    {
                        foreach (ApplicationRoleMapping mapping in app.ApplicationRoleMappingCollection)
                        {
                            roleList.Add( mapping.RoleName);
                        }
                    }
                }
                else
                {
                    roleList.AddRange(realmRoles);

                    if( mappings != null )
                    {
                        foreach (ApplicationRoleMapping mapping in app.ApplicationRoleMappingCollection)
                        {
                            bool bFound = false;
                            foreach( string role in roleList )
                            {
                                if( role == mapping.RoleName )
                                    bFound = true;
                            }

                            if(!bFound) roleList.Add(mapping.RoleName);
                        }
                    }

                    
                }
            }
            
            return (RoleCollection) roleList;
        }


        public SecurityAgent.RoleCollection GetRolesForUser(string applicationKey,  string operatorToken, string username)
        {
            
            SecurityToken token = Repository.GetToken(new Guid(operatorToken));
            
            if( token == null )
                 throw new FaultException<UnauthorizedAccessFault>(new UnauthorizedAccessFault{ Application = applicationKey, Object="Roles", Operation="GetRolesForUser"});

            string user;

            if( !String.IsNullOrEmpty(username) && username != token.UserName )
            {
                 if (!IsAuthorizedAdmin(applicationKey, operatorToken))
                    throw new FaultException<UnauthorizedAccessFault>(new UnauthorizedAccessFault{ Application = applicationKey, Object="Roles", Operation="GetRolesForOtherUser"});
                 
                 user = username;
            }
            else
                user = token.UserName;

            RoleCollection ret = new RoleCollection();
            string[] realmRoles = GetProvider(applicationKey).GetRolesForUser(user);

            ApplicationDefination app = Repository.GetApplicationDefinition(new Guid(applicationKey));

            List<string> roleList = new List<string>();

            if (app.UseRealmRole == (int)UseRealmRoleRequirement.Allways)
            {
                roleList.AddRange(realmRoles);
            }
            else
            {
                ApplicationRoleMapping[] mappings = Repository.GetApplicationRoleMappings(app.ApplicationKey);

                if (app.UseRealmRole == (int)UseRealmRoleRequirement.NotAllowed)
                {
                    if (mappings != null)
                    {
                        foreach (ApplicationRoleMapping mapping in app.ApplicationRoleMappingCollection)
                        {
                            foreach (string role in realmRoles)
                            {
                                if( role == mapping.RealmGroupName )
                                    roleList.Add(mapping.RoleName);
                            }
                            
                        }
                    }
                }
                else
                {
                    roleList.AddRange(realmRoles);

                    if (mappings != null)
                    {
                        foreach (ApplicationRoleMapping mapping in app.ApplicationRoleMappingCollection)
                        {
                            foreach (string role in realmRoles)
                            {
                                if (role == mapping.RealmGroupName)
                                    roleList.Add(mapping.RoleName);
                            }

                        }
                    }

                }
            }

            foreach( string s in roleList )
            {
                ret.Add(s);
            }

            return ret;
            
        }

        public string[] GetUsersInRole(string applicationKey, string operatorToken, string roleName)
        {
            if (!IsAuthorizedAdmin(applicationKey, operatorToken))
                throw new FaultException<UnauthorizedAccessFault>(new UnauthorizedAccessFault{ Application = applicationKey, Object="Roles", Operation="GetUsersInRole"});
           
           
            string[] roleList = Repository.GetRealmRoleName(new Guid(applicationKey), roleName);

            List<string> ret = new List<string>();

            if( roleList == null || roleList.Count() == 0 ) return null;

            RoleProvider provider = GetProvider(applicationKey);

            foreach( string role1 in roleList)
            {
                string[] users = provider.GetUsersInRole(role1);

                ret.AddRange(users);
            }

            return ret.ToArray();
            
        }

        public bool IsUserInRole(string applicationKey, string operatorToken, string username, string roleName)
        {
            SecurityToken token = Repository.GetToken(new Guid(operatorToken));
            
            if( token == null )
                 throw new FaultException<UnauthorizedAccessFault>(new UnauthorizedAccessFault{ Application = applicationKey, Object="Roles", Operation="GetRolesForUser"});

            string user;


            /// if user name is provided and doesn't match the user name for the token, then the userToken must belong to an admin user.  The services can pass the userToken they received to get the role information of the user.
            if( !String.IsNullOrEmpty(username) && username != token.UserName )
            {
                 if (!IsAuthorizedAdmin(applicationKey, operatorToken))
                    throw new FaultException<UnauthorizedAccessFault>(new UnauthorizedAccessFault{ Application = applicationKey, Object="Roles", Operation="GetRolesForOtherUser"});
                 
                 user = username;
            }
            else
                user = token.UserName;

            string[] roleList = Repository.GetRealmRoleName(new Guid(applicationKey), roleName);

            RoleProvider provider = GetProvider(applicationKey);

            foreach( string role1 in roleList)
            {
                if(provider.IsUserInRole(user, role1)) 
                    return true;
            }

            return false;
        }

        public void RemoveUsersFromRoles(string applicationKey, string operatorToken, string[] usernames, string[] roleNames)
        {
            if (!IsAuthorizedAdmin(applicationKey, operatorToken))
                throw new FaultException<UnauthorizedAccessFault>(new UnauthorizedAccessFault{ Application = applicationKey, Object="Roles", Operation="RemoveUsersFromRole"});

            ApplicationDefination appDef = Repository.GetApplicationDefinition(new Guid(applicationKey));
            if( appDef.UseRealmRole != (int) UseRealmRoleRequirement.Allways )
                throw new FaultException(new FaultReason("Application must use realm roles to allow this operation"));

            GetProvider(applicationKey).RemoveUsersFromRoles(usernames, roleNames);
        }

        public bool RoleExists(string applicationKey, string operationToken, string roleName)
        {
            if (!IsAuthorizedAdmin(applicationKey, operationToken))
                throw new FaultException<UnauthorizedAccessFault>(new UnauthorizedAccessFault{ Application = applicationKey, Object="Roles", Operation="RoleExists"});
           
            ApplicationDefination appDef = Repository.GetApplicationDefinition(new Guid(applicationKey));
            if( appDef.UseRealmRole != (int) UseRealmRoleRequirement.Allways )
                throw new FaultException(new FaultReason("Application must use realm roles to allow this operation"));

            return GetProvider(applicationKey).RoleExists(roleName);
        }
    }
}
